/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2017年8月24日
 * V4.0
 */
package com.jphenix.share.tools;

import java.util.HashMap;

import com.jphenix.kernel.script.ScriptLoader;
import com.jphenix.servlet.parent.ActionBeanParent;
import com.jphenix.share.lang.SString;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.servlet.IActionContext;

/**
 * 用于在线开发授权码验证以及其它公共方法
 * com.jphenix.share.tools.SuperProgramerTools
 * 
 * 2018-05-18 废弃Cookie存储授权码;调用脚本，验证失败时将失败原因作为异常信息抛出，并捕获到返回前台
 * 2018-09-12 密钥比对错误时，完善了日志中输出的日志信息，便于调试
 * 2018-10-30 验证授权码时，增加了特权免授权码模式（仅在内部Web服务使用）
 * 2019-01-24 修改了代码中父类的类路径
 * 2019-04-15 修改了调用脚本做身份验证，如果验证失败不抛异常，而是返回报错信息
 * 2019-07-11 增加了buildDevKey生成授权码方法
 * 2020-03-13 修改了从URL中获取参数
 * 
 * @author MBG
 * 2017年8月24日
 */
@ClassInfo({"2020-03-13 18:01","用于在线开发授权码验证以及其它公共方法"})
public class SuperProgramerTools {

	
	/**
	 * 生成授权码
	 * @param abp    动作类父类
	 * @param text   授权码明文
	 * @return       保存在授权码中的验证字符串
	 * 2019年7月11日
	 * @author MBG
	 */
	public static String buildDevKey(ActionBeanParent abp,String text) {
		if(abp==null) {
			return "";
		}
		return MD5.getMD5Value(abp.p("project_code")+text);
	}

	/**
	 * 是否允许访问在线开发功能
	 * 
	 * 如果提交过来的值中包含 _pkey_set_cookie_=1 在验证成功后
	 * ，会将提交的授权码做Base64加密后，保存到主键为  [产品代码]_op_key_的cookie中
	 * 产品代码保存在配置文件：<project_code></project_code> 中
	 * 
	 * @return true允许访问
	 * 2017年8月24日
	 * @author MBG
	 */
	public static boolean allow(ActionBeanParent abp) {
		if(abp==null) {
			return false;
		}
		//动作上下文
		IActionContext ctx = abp.getActionContext();
		try {
		    if(abp.boo(abp.p("safe_link/disabled"))){
		        if(abp.boo(ctx.getUrlParameter("json"))) {
		        	abp.out(-403,"禁止访问");
		        }else{
		        	ctx.getResponse().sendError(403,ctx.getRequest());
		        }
		        return false;
		    }
		    
		    //如果在配置文件中配置disable_outsite_develop值为true，则禁止从Servlet容器
		    //访问调用该方法（继承AdminBeanParent父类）的动作类
		    //通常需要在配置文件中配置native_web_server_port的值，即程序内部的Web服务器
		    //监听端口，从这个监听端口可以访问这些被禁用的动作类
		    //通常用于生产环境中，避免外部可以访问这些高风险的动作。在网络内部，可以通过
		    //港口（在局域网内部并未开放在外网的，支持开发模式的项目）为生产环境热部署
		    if(abp.boo(abp.p("disable_outsite_develop")) 
		    		&& !ctx.getRequest().isNativeServer()) {
		    	abp.warning("The Properties [disable_outsite_develop] Set true value from Config File. Can Not Visit Admin Website From Public Port");
		        if(abp.boo(ctx.getUrlParameter("json"))) {
		        	abp.out(-403,"禁止访问");
		        }else{
		        	ctx.getResponse().sendError(403,ctx.getRequest());
		        }
		    	return false;
		    }
		    
		    if(abp.boo(abp.p("safe_link/key_from_db"))) {
		    	//如果是企业内网使用的内部管理平台，只运行在内网。目前只校验验证状态，并不是
		    	//每次验证授权码。切记禁止在外网使用这种模式。否则会话中一旦标记admin_login_ok为1
		    	//就能任意编写程序
		    	
		        //脚本加载器
		        ScriptLoader sl = abp.bean(ScriptLoader.class);
		    	if(sl!=null) {
		    		//构建传参容器
		    		HashMap<String,Object> paraMap = new HashMap<String,Object>();
		    		paraMap.put("key",abp.str(ctx.getUrlParameter("_p_key_")));
		    		paraMap.put("nativeWeb",ctx.getRequest().isNativeServer()?"1":"0"); //是否从内置服务器访问
		    		paraMap.put("userInfo",ctx.getSessionAttribute("_user_info_"));    //放入用户信息包
		    		
		    		//调用服务验证是否授权
		    		try {
		    			//调用验证身份
		    			String res = SString.valueOf(sl.invokeScript(abp,"N100122",paraMap));
		    			if(res.length()<1) {
		    				return true;
		    			}
		    			//返回了报错信息
		    			abp.out(-403,res);
		    		}catch(Exception e) {
		    			e.printStackTrace();
		    			abp.out(-500,e.getMessage());
		    		}
		    	}
		    	return false;
		    }
		    
		    /* 以下还是采用配置文件中的唯一授权码进行验证 */
			String keyName  = abp.p("safe_link/key");   //参数主键名
			String checkKey = abp.p("safe_link/value"); //校验值
			if(keyName.length()<1) {
				keyName = "_p_key_";
			}
			if(checkKey.length()<1) {
				abp.log("开发平台被设置为禁用，配置文件中没有配置safe_link/value");
			  if(abp.boo(ctx.getUrlParameter("json"))) {
				  abp.outs(-403,"禁止访问");
			  }else{
				  ctx.getResponse().sendError(403,ctx.getRequest());
			  }
			  return false;
			}
			//从会话中获取管理员登录主键
			String key = abp.str(ctx.getSessionAttribute(keyName));
			if(key.length()<1) {
				key = ctx.getParameter(keyName);
			}
			if(!MD5.getMD5Value(key).equals(checkKey)) {
				abp.log("进入管理台验证失败 配置文件:["+checkKey+"] 提交值：["+key+"]=>["+MD5.getMD5Value(key)+"]");
			  if(abp.boo(ctx.getUrlParameter("json"))) {
				  abp.outs(-402,"验证失败");
			  }else{
				  ctx.getResponse().sendError(403,ctx.getRequest());
			  }
			  return false;
			}
			//验证成功，将验证key放入会话
			ctx.setSessionAttribute(keyName,key);
			
			//标记后台登录成功。在LogEventTask类中用了这个属性值，这样在获取日志任务动作中
			//可以很快认定是否登录
			ctx.setSessionAttribute("admin_login_ok","1");
			return true;
		}catch(Exception e) {
			abp.outs(-500,"验证发生异常");
			e.printStackTrace();
		}
		return false;
	}

}
